﻿using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using FStore.Utils;

namespace FStore.Formats
{
    /// <summary>
    /// Allows an object to be stored as a base-64 encoded string
    /// </summary>
    public sealed class BinarySerFormat : IFlatFormat
    {
        #region IFlatFormat Members

        public string Decompose<T>(T obj)
        {
            return Decompose(typeof(T), obj);
        }

        public string Decompose(Type type, object obj)
        {
            // Param checking
            if (type == null)
            {
                throw new ArgumentNullException("type");
            }

            if (obj == null)
            {
                throw new ArgumentNullException("obj");
            }

            if (obj.GetType() != type)
            {
                throw new ArgumentException("obj is not of correct type", "obj");
            }

            try
            {
                // Create the binary formatter
                BinaryFormatter bf = new BinaryFormatter();
                // Memory stream
                MemoryStream ms = new MemoryStream();
                // Serialise the object
                bf.Serialize(ms, obj);
                // Reset the stream
                ms.Position = 0;
                
                // Return the base-64 string of the serialised object
                return Convert.ToBase64String(ms.GetBuffer());
            }
            catch (Exception ex)
            {
                throw new Exception(
                    "Failed to Decompose object of type '{0}' - {1}".UFormat(
                        obj.GetType().FullName,
                        ex.Message));
            }   
        }

        public T Compose<T>(string str)
        {
            return (T)Compose(typeof(T), str);
        }

        public object Compose(Type type, string str)
        {
            // Param checking
            if (type == null)
            {
                throw new ArgumentNullException("type");
            }

            if (string.IsNullOrEmpty(str))
            {
                throw new ArgumentException("Cannot compose string as it was null or empty");
            }

            try
            {
                // Create the binary formatter
                BinaryFormatter bf = new BinaryFormatter();
                // Gets the byte array from the base-64 string
                byte[] b = Convert.FromBase64String(str);
                // Write to the memory stream
                MemoryStream ms = new MemoryStream();
                ms.Write(b, 0, b.Length);
                ms.Position = 0;
                // Deserialise the object
                object obj = bf.Deserialize(ms);
                ms.Dispose();
                return obj;
            }
            catch (Exception ex)
            {
                throw new Exception(
                    "Failed to compose object of type '{0}' from string '{1}' - {2}".UFormat(
                        type.FullName,
                        str,
                        ex.Message));
            }
        }

        #endregion  
    }
}